package com.eci.context.context.core;

import org.hibernate.HibernateException;
import org.hibernate.property.access.internal.PropertyAccessStrategyBasicImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyChainedImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyFieldImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMapImpl;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.transform.AliasedTupleSubsetResultTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Clob;
import java.time.temporal.Temporal;
import java.util.Arrays;

@SuppressWarnings("rawtypes")
public class UnderscoreAliasTransformer  extends AliasedTupleSubsetResultTransformer{
 
	private static final long serialVersionUID = 1L;
	private static final Logger logger = LoggerFactory.getLogger(UnderscoreAliasTransformer.class);
	
	
	private final Class resultClass;
	private boolean isInitialized;
	
	private String[] aliases;
	private Setter[] setters;
 
	public UnderscoreAliasTransformer(Class resultClass) {
		if (resultClass == null) {
			throw new IllegalArgumentException("resultClass cannot be null");
		}
		this.isInitialized = false;
		this.resultClass = resultClass;
	}


	@Override
	public boolean isTransformedValueATupleElement(String[] aliases, int tupleLength) {

		return false;
	}
	 
	
	@Override
	public Object transformTuple(Object[] tuple, String[] aliases) { 
		Object result; 
		try {
			if (!isInitialized) {
				initialize(aliases);
			} else {
				check(aliases);
			} 
			result = resultClass.newInstance(); 
			transformer(aliases, tuple, result);

		} catch (InstantiationException | IllegalAccessException e) {
			logger.error("cannot  instantiates ：" + resultClass.getName(), e);
			throw new HibernateException("Could not instantiate resultclass: " + resultClass.getName());
		}
		return result;
	}
	 
	
	private void initialize(String[] aliases) {

		PropertyAccessStrategyChainedImpl pasci = new PropertyAccessStrategyChainedImpl(PropertyAccessStrategyBasicImpl.INSTANCE,
				PropertyAccessStrategyFieldImpl.INSTANCE,PropertyAccessStrategyMapImpl.INSTANCE);

		this.aliases = new String[aliases.length];
		setters = new Setter[aliases.length];
		Field[] allFields = TransformSupport.getAllFields(resultClass);
		 
		if (allFields == null || allFields.length == 0) {
			throw new RuntimeException("POJO [" + resultClass.getName() + "] does not contain any attributes.");
		}
		
		for (int i = 0; i < aliases.length; i++) {
			String alias = aliases[i];
			if (alias != null) {
				this.aliases[i] = alias;
				setters[i] = TransformSupport.getSetterByColumnName(resultClass,allFields, alias, pasci);
			}
		}
		isInitialized = true;
	}
	
	
	
	private void check(String[] aliases) {
		
		if (!Arrays.equals(aliases, this.aliases)) {
			
			throw new IllegalStateException("aliases are different from what is cached; aliases="+ Arrays.asList(aliases) 
				+ " cached=" + Arrays.asList(this.aliases));
		}
	}
	
	
	private void transformer( String[] aliases, Object[] tuple,Object result) {
		  
		for (int i = 0; i < aliases.length; i++) {
			if (setters[i] != null && tuple[i] != null) {
				Method method = setters[i].getMethod();
				if (method == null) {
					throw new RuntimeException("Could not find DB Column:" + aliases[i] + " Setter Method!");
				}
				Class<?> paramType = method.getParameterTypes()[0];
				Class<?> tupleClass = tuple[i].getClass();
				// 若目标参数类型和当前参数类型不匹配 ,尝试进行转换
				if (!paramType.equals(tupleClass)) {
					if (Number.class.isAssignableFrom(tupleClass)) {
						Number num = (Number) tuple[i];
						Object numVal =  TransformSupport.getNumberValue(paramType, num);
						if(numVal == null) {
							throw new RuntimeException("Unsupported type conversion ：" + tuple[i].getClass());
						}
						setters[i].set(result,numVal,null); 
					} else if(Character.class.equals(tupleClass)){ 
						if(String.class.equals(paramType)){
							setters[i].set(result, String.valueOf(tuple[i]), null); 
						}
						else if (Boolean.class.equals(paramType) || boolean.class.equals(paramType)) {
							
							Boolean blValue=TransformSupport.getBooleanValue(String.valueOf(tuple[i]));
							if(blValue == null) {
								throw new RuntimeException("Unsupported boolean type conversion ：" + tuple[i].getClass());
							}
							setters[i].set(result, blValue, null); 
						} 
					}
					//如果tuple为参数的子类,直接设置 如java.util.Date; java.sql.Date;
					else if (paramType.isAssignableFrom(tupleClass)) { 
						setters[i].set(result, tuple[i], null); 
					} 
					else if(Temporal.class.isAssignableFrom(paramType)){
						
						Temporal xtime = TransformSupport.getJava8TimeValue(paramType, tuple[i]);
						if(xtime == null) {
							throw new RuntimeException("Unsupported java8time type conversion ：" + tuple[i].getClass());
						} 
						setters[i].set(result, xtime, null);
					}
					//处理数据库类型定义为大字段Clob的数据，将其转换成字符串类型
					else if (tuple[i] instanceof Clob) {
						Clob clob = (Clob) tuple[i];
						setters[i].set(result, TransformSupport.clobToString(clob), null);
					} else {
						throw new RuntimeException("Unsupported type conversion ：" + tuple[i].getClass());
					}
				} else {
					setters[i].set(result, tuple[i], null);
				}
			}
		}
		
	}

	@Override
	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o == null || getClass() != o.getClass()) {
			return false;
		}
		UnderscoreAliasTransformer that = (UnderscoreAliasTransformer) o;
		return resultClass.equals(that.resultClass) && Arrays.equals(aliases, that.aliases);
	}

	@Override
	public int hashCode() {
		int result = resultClass.hashCode();
		result = 31 * result + (aliases != null ? Arrays.hashCode(aliases) : 0);
		return result;
	}

}
